#PR Automation Project
#DATE: January 7-14, 2008
#Author: Coltin Caverhill
#Currently Maintained by: ...

use URI;								#Package used to process URLs (extract and create)
use LWP::UserAgent;				#Package used to act as a pseudo browser to interact with web servers
use HTTP::Cookies::Netscape;	#Package used to send/retrieve cookies to do login/logout

my $tsOld;	#Timestamps, old anew
my $tsNew;

sub trim($) #Removes whitespace from the begining and end of a string
{
	my $string = shift;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}

sub extract_PRs { #Processes a PR list, storing all provided data into a hash
	$pr =		@_[0];
	$inf =	@_[1];

	while (<$inf>) {
		$cLine = trim($_);
		print $cLine,"\n";
		if (index($cLine,'<tr class="') != -1) {
			my %tmp;	#Creates a hash in memory
			my @seq = reverse(qw (Assigned-To Status Resolution RelNote));
			while (<$inf>) {
				my $myCur = trim($_);
				last if ($myCur eq '</tr>');
				my $tag = substr($myCur,0,index($myCur,'">')+2);
				if ($tag eq '<td style="white-space: nowrap">') {
					my $val = substr($myCur, index($myCur,'">')+2,length($myCur));
					my $aPop = pop(@seq);
					if ($val eq '') {
						$val='NONE';
					} elsif ($aPop eq 'Assigned-To') { #Replaces SGM code for actual value ($#64; => @)
						$val =~ s/&#64;/@/;
					}
					$tmp{$aPop}=$val;
				} elsif ($tag eq '<td class="first-child">') {
					my $tline = trim(<$inf>);
					$tline = substr($tline,index($tline,'">')+2,100);
					$tmp{'ID'}=substr($tline,0,index($tline,'</a>'));
				} elsif (substr($myCur,0,5) eq '<td >') {
					$tmp{'RelNote'} = '"<b>'.substr($myCur,5,1000).'</b>"';
				}
			}
			$tmp {'Timestamp'}='1';
			push (@$pr,\%tmp);	#Loads the hash reference into the PR array/list. All PRs are retrieved by accessing references in the array.
			#Example: $$pr[1]{'Status'}
		}
	}
}


sub StorePRS {	#Loads PRs from PRzilla
	$conf = @_[0];
	my $webURL = trim(<$conf>);
	my $email=trim(<$conf>);
	my $pword=trim(<$conf>);
	my $Success="PR List";
	my $browser = LWP::UserAgent->new( );
	my $cookies = HTTP::Cookies::Netscape->new('file' => 'cookies.txt');
	$browser->cookie_jar($cookies);

	my $url = URI->new($webURL);
	my $loginResp = $browser->post('http://przilla.ott.qnx.com/bugzilla/index.cgi?GoAheadAndLogIn=1',
		[
			'Bugzilla_login' => $email,
			'Bugzilla_password' => $pword,
			'GoAheadAndLogIn' => 'Log in',
		],
	);
	my $tBrow = LWP::UserAgent->new;
	$tBrow->cookie_jar($cookies);
	my $tResp = $tBrow->post (
		'http://przilla.ott.qnx.com/bugzilla/colchange.cgi',
		[
		'rememberedquery' => $url,
		
		'Bugzilla_login' => $email,
		'Bugzilla_password' => $pword,
		'GoAheadAndLogIn' => 'Log in',
		
		'column_assigned_to' => 'on',
		'column_bug_status' => 'on',
		'column_resolution' => 'on',
		'column_status_whiteboard' => 'on',
		],
	);

	my $cont = $browser->get($url);

	open OUF, "> PR_LIST.html";
	print OUF $cont->content;
	close OUF;

	die "Error:", $cont->title unless $cont->title eq $Success;
}

sub extract_docs_online { #Stores docs online, and then into an array
	my $docRef =@_[0];
	my $inf = 	@_[1];
	runFormChange();
	print "ahhhhhhhhh";
	$inf = <$inf>;
	my $brows = LWP::UserAgent->new();
	open OUF, "> docs.html";
	print OUF $brows->get(URI->new($inf))->content;
	close OUF;
	open INF, "docs.html";
	@$docRef = <INF>;
	close INF;
}

sub extract_docs { #Loads the contents of a file into an array
	my $docRef =@_[0];
	my $inf = 	@_[1];
	$inf = <$inf>;
	open INF, $inf;
	@$docRef = <INF>;
	close INF;
}

sub prod_backup { #Creates a backup of a file
	my $inf = @_[0];
	my $fName = <$inf>;
	open INF, $fName;
	open OUF, "> back.html";
	print OUF <INF>;
	close INF;
	close OUF;
	return $fName;
}

sub InArray { #Determines if something is an an array, returns the first index+1 ([$i-1] to use)
	$arRef = 	@_[0];
	$searchKey =@_[1];
	my $i = -1;
	for (@$arRef) {
		$i++;
		return ($i+1) if ($searchKey eq $$_{'tag'});
	}
	return 0
}

sub removePR_viaAny { #Removes all PRs that match the criteria
	my $prRef = @_[0];
	my $hashLook = @_[1];
	my $match = @_[2];
	my $i = scalar(@$prRef);
	for (reverse(@$prRef)) {
		$i--;
		if ($$_{$hashLook} eq $match) {
			splice(@$prRef,$i,1);
		}
	}
}

sub Clause { #Produces a PERL boolean clause for eval()
	my $curAdd = @_[0];
	my $prRef = @_[1];
	my $evaLine = '';

	while ($curAdd) {
		$fLet = substr($curAdd, 0, 1);

		if ($fLet eq ' ') { #Spaces, don't need em.
			$curAdd = substr($curAdd,1,length($curAdd)-1);
		} elsif ($fLet eq '&' ||
					$fLet eq '(' ||
					$fLet eq ')' ||
					$fLet eq '|' ||
					$fLet eq '=' ||
					$fLet eq '!' ||
					$fLet eq '>' ||
					$fLet eq '<') { #This could be done with index and a string of symbols, but it looks cool =)
			$evaLine = $evaLine.$fLet;
			$curAdd = substr($curAdd,1,length($curAdd)-1);
		} elsif ($fLet eq '\'') { #Single quotes are special, see documentation
			$curAdd = substr($curAdd,1,length($curAdd)-1);
			my $curInd = substr($curAdd,0,index($curAdd,'\''));
			$curAdd = substr($curAdd,index($curAdd,'\'')+1,length($curAdd));
			if ($$prRef{$curInd}) {
				$evaLine = $evaLine.'\''.$$prRef{$curInd}.'\'';
			} else {
				$evaLine = $evaLine.'\''.$curInd.'\'';
			}
		} else { #It must be a string or error, pretend its a string
			$curAdd =~ /(\w+)/; #Match string
			my $curInd = $1;
			$curAdd = substr($curAdd,length($curInd),length($curAdd));
			if ($$prRef{$curInd}) {
				$evaLine = $evaLine.'\''.$$prRef{$curInd}.'\'';
			} else {
				if ($curInd eq 'old') {
					$evaLine = $evaLine.'\''.$tsOld.'\'';
				} elsif ($curInd eq 'current') {
					$evaLine = $evaLine.'\''.$tsNew.'\'';
				} else {
					$evaLine = $evaLine.'\''.$curInd.'\'';
				}
			}
		}
	}
	$evaLine =~ s/==/eq/g; #Convert == to eq
	$evaLine =~ s/!=/ne/g; #Convert != to ne	
	return $evaLine;
}

sub DocList { #Stores the PR list into a file
	my $listRef = @_[0];
	my $loc = @_[1];
	open PRINF, "> $loc";
	for (@$listRef) {
		my %tH = %$_;
		print PRINF $tH{'ID'},"\t",$tH{'Status'},"\t",$tH{'Resolution'},"\t",$tH{'Assigned-To'},"\t",$tH{'RelNote'},"\n";
	}
	close PRINF;
}

sub StoreTags { #Loads PRs into a hash, and then stores references of the hash in an array
	my $tagRef = @_[0];
	my $conf = @_[1];

	while (<$conf>) {
		my %tmp;
		my @tmp2;
		$tmp{'tag'} = trim($_);
		$tmp{'add'} = trim (<$conf>);
		$tmp{'add'} = substr($tmp{'add'},5,length($tmp{'add'}));
		$tmp{'rem'} = trim (<$conf>);
		$tmp{'PRs'} = \@tmp2;
		push (@tmp2,1);
		push(@$tagRef,\%tmp);
	}
	close $conf;
}

sub updatePR { #Used to update DOC PRs with Online PR stats
	my $prRef = @_[0];
	my $prListRef = @_[1];

	for (@$prListRef) {
		if ($$_{'ID'} == $$prRef{'ID'}) {
			$$prRef{'Status'} = $$_{'Status'};
			$$prRef{'Resolution'} = $$_{'Resolution'};
			$$prRef{'Assigned-To'} = $$_{'Assigned-To'};
			return
		}
	}
}

sub IdInArray {
	$PrListRef = @_[0];
	$idSearch = @_[1];
	my $i = -1;
	for (@$PrListRef) {
		$i++;
		if ($idSearch eq $$_{'ID'}) {
			return ($i+1);
		}
	}
	return 0;
}

#Program starts here
my @PR_LIST_ON;	#The list of PRs extracted from the online search
my @PR_LIST_DOC;	#The list of PRs extracted from the Docs
my @tagz;			#Holds search tag, and add/remove criteria, as well as all the PRs for each header
my @Reports;		#Contains everything thats been removed, and updated

open CONF, "config.config";
StorePRS(CONF);

open INF, "PR_LIST.html";
extract_PRs(\@PR_LIST_ON, INF);
close INF;
DocList(\@PR_LIST_ON, "prinf.txt");

my $infFileName = prod_backup(CONF);
$tsOld = trim(<CONF>);
$tsNew = trim(<CONF>);
StoreTags(\@tagz, CONF);

open DOCS, "back.html";
open UPDA, "> output.html";
open REM_STACK, '>> remHash-'.$tsNew.'.txt';
while (<DOCS>) {
	my $cLine = trim($_);
	my $tagSearch = substr($cLine, index($cLine,'">')+2, index($cLine,'</h')-2-index($cLine,'">')),"\n";
	if (InArray(\@tagz,$tagSearch)) { #Indicates a new search criteria for PRs
		print UPDA $_;
		while (<DOCS>) {
			$curHTML = $_;
			$curLine = trim ($curHTML);
			last unless $curLine ne '</ul>';
			if (substr ($curLine, 0, 3) eq '<li') {
				my %tmp;
				$tmp{'ID'}=trim(substr($curLine, index($curLine,'>')+1, index($curLine,'&mdash;')-index($curLine,'>')-1));
				if (index($curLine,"<!--timestamp='") != -1) {
					$tmp{'Timestamp'} = substr($curLine, index($curLine, "<!--timestamp='")+15, index($curLine, "'-->")-index($curLine, "<!--timestamp='")-15 );
					$tmp{'RelNote'} = trim(substr ($curLine, index($curLine,'&mdash;')+8, index($curLine, "<!--timestamp='")-8-index($curLine,'&mdash;')));
				} else {
					$tmp{'Timestamp'} = $tsOld;
					$tmp{'RelNote'} = trim(substr ($curLine, index($curLine,'&mdash;')+8, length ($curLine)));
				}
				if ($tsNew eq $tmp{'Timestamp'} || $tagz[InArray(\@tagz,$tagSearch)-1]{'rem'} == 0) {
					my $fId = IdInArray(\@PR_LIST_ON,$tmp{'ID'});
					if ($fId) {
						$PR_LIST_ON[$fId-1]{'RelNote'} = $tmp{'RelNote'};
					} else {
						my $tempTagRefArray = $tagz[InArray(\@tagz,$tagSearch)-1]{'PRs'};
						push (@$tempTagRefArray, \%tmp);
					}
				} else {
					#removePR_viaAny(\@PR_LIST_ON, 'ID', $tmp{'ID'});
					print REM_STACK $tmp{'ID'},"\n";
					push (@Reports, "$tmp{'ID'}_has_been_removed_from_$tagSearch");
				}
			} else {
				print UPDA $curHTML;
			}
		}
		print UPDA "</ul>\n";
	} else {
		print UPDA $_;
	}
}
close DOCS;
close UPDA;
close REM_STACK;

open REM_STACK, 'remHash-'.$tsNew.'.txt';
for (<REM_STACK>) {
	#print $_;
	removePR_viaAny(\@PR_LIST_ON, 'ID', trim($_));
}
close REM_STACK;

print "\n";

for (@tagz) { #Update the timestamps of tagz{'PRs'}
	my $curTag = $$_{'add'};
	my $curPrList = $$_{'PRs'};
	for (@$curPrList) {
		$$_{'Timestamp'} = $tsNew;
	}
}

for (@PR_LIST_ON) { #Updates timestamps for PRs
	$$_{'Timestamp'} = $tsNew;
	#print $$_{'Timestamp'},"\n";
}

for (@tagz) {
	my $curTag=$$_{'add'};
	my $curArRef=$$_{'PRs'};
	for (@PR_LIST_ON) {
		if (eval (Clause($curTag,$_))) {
			#print $$_{'ID'}," ",$$_{'Resolution'}," ",$$_{'Status'}," ",$$_{'Assigned-To'},"\n";
			push(@$curArRef,$_);
			push (@Reports, "$$_{'ID'}_has_been_updated");
		}
	}
}

open DOCS, "back.html";
open OUFF, "> Final.html";

while (<DOCS>) { #Build the .SGM file
	my $curHTML = $_;
	my $curLine = trim($curHTML);
	my $tagSearch = substr($curLine, index($curLine,'">')+2, index($curLine,'</h')-2-index($curLine,'">')),"\n";
	my $sN = InArray(\@tagz,$tagSearch);
	if ($sN) {
		print OUFF $curHTML;
		while (<DOCS>) {
			my $curHTML2 = $_;
			my $curLine2 = trim ($curHTML2);
			last if $curLine2 eq '<ul>';
			print OUFF $curHTML2;
		}
		print OUFF "<ul>";
		while (<DOCS>) { #Burns through the SGM file
			my $curHTML2 = $_;
			my $curLine2 = trim($curHTML2);
			last if $curLine2 eq '</ul>';
		}
		my $tprRef = $tagz[$sN-1]{'PRs'};
		my %curInputs;
		my @curOrder;
		for (@$tprRef) {
			if ($_!=1) {
				$curInputs{$$_{'ID'}} = "\n\t<li>".$$_{'ID'}." &mdash; ".$$_{'RelNote'}."<!--timestamp='".$$_{'Timestamp'}."'-->","\n";
				push (@curOrder, $$_{'ID'});
			}
		}
		my @tempOrder = reverse(sort(@curOrder));
		my $space = (($tempOrder[0] - ($tempOrder[0] % 1000))/1000);
		for (@tempOrder) {
			if ($space > (($_ - ($_ % 1000))/1000)) {
				print OUFF "\n<p>";
				$space = (($_ - ($_ % 1000))/1000);
			}
			print OUFF $curInputs{$_};
		}
		print OUFF "\n</ul>\n";
	} else {
		print OUFF $curHTML;
	}
}

close DOCS;
close OUFF;

print "Done\n";

unlink "Output.html";

open REP, ">> Report_$tsNew.txt";
print REP "\nNew Search\n";
for (reverse(sort(@Reports))) {
	$_ =~ s/_/ /g;
	#print $_,"\n";
	print REP $_,"\n";
}